#include "CentralCache.h"

// 单例，饿汉模式
CentralCache CentralCache::_centralCache;
CentralCache* CentralCache::GetInstance()
{
    return &_centralCache;
}
// 将cc中内存块给tc
size_t CentralCache::FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size)
{
    size_t alignedSize = Tool::RoundUp(size);
    size_t index = Tool::Index(alignedSize);

    _spanLists[index].GetMtx().lock(); // 桶锁

    Span* span = GetOneSpan(_spanLists[index], size); // 获取一个span

    if (!span || !span->_freeList)
        return 0;
    start = end = span->_freeList;
    size_t actualNum = 1;
    for (; actualNum < batchNum && Tool::NextObj(end); actualNum++)
        end = Tool::NextObj(end);
    span->_freeList = Tool::NextObj(end); // 更新span
    span->_useCount += actualNum;
    Tool::NextObj(end) = nullptr;

    _spanLists[index].GetMtx().unlock(); // 解锁

    return actualNum;
}

// 从cc中找到一个非空的span
Span* CentralCache::GetOneSpan(SpanList& list, size_t size)
{
    // 找非空span
    Span* it = list.Begin();
    while (it != list.End())
    {
        if (it->_freeList)
            return it;
        it = it->_next;
    }

    // 没有非空的span，从pc中获得新的span
    size_t index = Tool::Index(Tool::RoundUp(size));
    _spanLists[index].GetMtx().unlock(); // 从pc申请内存，先解桶锁
    size_t nPage = Tool::NumMovePage(size); // 申请页数

    Span* nSpan = nullptr;
    {
        std::unique_lock<std::mutex> lock(PageCache::GetInstance()->GetMtx()); // pc锁
        nSpan = PageCache::GetInstance()->NewSpan(nPage);  // 新span
        // cc获得新的span后pc就解锁了，如果这时pc合并span，而这个span的useCount为0， 就可能会被合并
        // 将useCount设为1，表示这个span已经不在pc了
        // cc中使用span的内存块useCount+1，释放-1，当useCount=1时，将span返回给pc,再将useCount设为0
        // pc中只有当useCount=0时才会合并
        nSpan->_useCount = 1;
    }
    // 从pc申请的span没有划分为内存块
    char* start = (char*)(nSpan->_id << page_shift);
    char* end = (char*)(start + (nSpan->_nPage << page_shift));

    nSpan->_objSize = size; // 设置内存块大小

    nSpan->_freeList = start;
    char* next = start + size;
    while (next < end)
    {
        Tool::NextObj(start) = next;
        start = next;
        next += size;
    }
    Tool::NextObj(start) = nullptr;

    _spanLists[index].GetMtx().lock();
    list.PushFront(nSpan);
    return nSpan;
}

//回收tc的内存块到pc的span
void CentralCache::ReleaseListToSpan(void* start, size_t size)
{
    size_t index = Tool::Index(size);

    _spanLists[index].GetMtx().lock();
    while (start)
    {
        void* next = Tool::NextObj(start);
        Span* span = PageCache::GetInstance()->MapObjectToSpan(start); // 在map中查找地址对应的span

        Tool::NextObj(start) = span->_freeList;
        span->_freeList = start;
        --span->_useCount;
        start = next;

        if (span->_useCount == 1) // span在cc中没有被使用
        {
            _spanLists[index].Erase(span); // cc的表中删除span
            span->_next = span->_prev = nullptr;
            span->_freeList = nullptr;// 内存块回收无序，_freeList已经不需要，可以通过pageid和npage知道内存地址
            span->_useCount = 0; // usecount置0
            span->_objSize = 0;

            {
                std::unique_lock<std::mutex> lock(PageCache::GetInstance()->GetMtx());
                PageCache::GetInstance()->ReleaseSpanToPageCache(span); // pc回收span
            }
        }
    }
    _spanLists[index].GetMtx().unlock();
}